package com.ray.depot.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.ray.depot.enums.RecordType;
import com.ray.depot.service.StockDetailSerializeService;
import com.ray.depot.service.StockDetailService;
import com.ray.depot.service.StockInOutSerializeService;
import com.ray.depot.service.StockInOutService;
import com.ray.depot.service.StockSerializeService;
import com.ray.depot.service.StockService;
import com.ray.depot.table.customMapper.SelectStockMapper;
import com.ray.depot.table.dto.StockDto;
import com.ray.depot.table.entity.InOut;
import com.ray.depot.table.entity.InOutRecord;
import com.ray.depot.table.entity.InOutSerialize;
import com.ray.depot.table.entity.Stock;
import com.ray.depot.table.entity.StockDetail;
import com.ray.depot.table.entity.StockDetailSerialize;
import com.ray.depot.table.entity.StockExample;
import com.ray.depot.table.entity.StockExample.Criteria;
import com.ray.depot.table.entity.StockInOut;
import com.ray.depot.table.entity.StockInOutSerialize;
import com.ray.depot.table.entity.StockSerialize;
import com.ray.depot.table.mapper.StockMapper;
import com.xiaoleilu.hutool.util.ObjectUtil;
import com.zjhc.flyhong.common.function.enums.DeleteEnum;
import com.zjhc.flyhong.common.function.exception.BussinessException;
import com.zjhc.flyhong.common.function.operator.LoginUser;
import com.zjhc.flyhong.common.function.operator.PageDto;
import com.zjhc.flyhong.common.util.UUIDUtil;
import com.zjhc.flyhong.common.web.ErrorMsg;
import com.zjhc.flyhong.common.web.Result;
import com.zjhc.flyhong.common.web.SuccessMsg;
import com.zjhc.flyhong.common.web.base.BaseService;

@Service
public class StockServiceImpl extends BaseService implements StockService {
	/** 日志对象 **/
	private static Logger logger = Logger.getLogger(StockServiceImpl.class);

	@Autowired
	private StockMapper stockMapper;
	@Autowired
	private StockDetailService stockDetailService;
	@Autowired
	private StockSerializeService stockSerializeService;
	@Autowired
	private StockDetailSerializeService stockDetailSerializeService;
	@Autowired
	private StockInOutService stockInOutService;
	@Autowired
	private StockInOutSerializeService stockInOutSerializeService;
	@Autowired
	private SelectStockMapper selectStockMapper;

	@Transactional
	public Result<Object> addStock(Stock params, LoginUser user) {
		Result<Object> result = null;
		result = new Result<Object>(0, SuccessMsg.OP_SUCCESS);
		params.setTenantId(user.getTenantId());
		params.setStockId(UUIDUtil.getUUID());
		params.setIsDelete(DeleteEnum.USE.getValue());
		this.setUserForAdd(params, user);
		int count = stockMapper.insertSelective(params);
		if (count != 1) {
			logger.error("StockServiceImpl=>addStock is error");
			throw new BussinessException(-1, ErrorMsg.INSERT_ERROR);
		}
		return result;
	}

	@Transactional(propagation = Propagation.NOT_SUPPORTED)
	public Stock queryStock(String id, LoginUser user) {
		return stockMapper.selectByPrimaryKey(id);
	}

	@Transactional
	public Result<Object> updateStock(Stock params, LoginUser user) {
		Result<Object> result = null;
		result = new Result<Object>(0, SuccessMsg.OP_SUCCESS);
		this.setUserForUpdate(params, user);
		int count = stockMapper.updateByPrimaryKeySelective(params);
		if (count != 1) {
			logger.error("StockServiceImpl=>editStock is error");
			throw new BussinessException(-1, ErrorMsg.INSERT_ERROR);
		}
		return result;
	}

	@Transactional
	public Result<Object> deleteStock(String id, LoginUser user) {
		Result<Object> result = null;
		result = new Result<Object>(0, SuccessMsg.OP_SUCCESS);
		Stock params = new Stock();
		params.setStockId(id);
		params.setIsDelete(DeleteEnum.DELETE.getValue());
		this.setUserForUpdate(params, user);
		int count = stockMapper.updateByPrimaryKeySelective(params);
		if (count != 1) {
			logger.error("StockServiceImpl=>deleteStock is error");
			throw new BussinessException(-1, ErrorMsg.INSERT_ERROR);
		}
		return result;
	}

	@Transactional(propagation = Propagation.NOT_SUPPORTED)
	public PageDto<Stock> queryStock(Stock params, PageDto<Stock> page, LoginUser user) {
		StockExample example = new StockExample();
		Criteria criteria = example.createCriteria();
		// 超级管理员能查看全部
		if (!user.isSuperManager()) {
			criteria.andTenantIdEqualTo(user.getTenantId());
		}
		criteria.andIsDeleteEqualTo(DeleteEnum.USE.getValue());
		this.setExamplePage(example, page);
		page.setTotal(stockMapper.countByExample(example));
		page.setRecords(stockMapper.selectByExample(example));
		return page;
	}

	@Override
	@Transactional
	public void storeIn(InOutRecord record, List<InOut> inOuts, List<InOutSerialize> inOutSerializes, LoginUser user) {
		// Map<String,Stock> stockMap= new HashMap<>();
		// 物资入库
		for (InOut inOut : inOuts) {
			// 查询仓库是否存在物料
			Stock stock = this.queryStock(inOut.getMaterialId(), inOut.getWarchouseId(), user);
			// 库存空需要新增
			if (ObjectUtil.isNull(stock)) {
				stock = new Stock();
				stock.setStockId(UUIDUtil.getUUID());
				stock.setMaterialId(inOut.getMaterialId());// 物料
				stock.setWarchouseId(inOut.getWarchouseId());// 仓库
				// stock.setLibraryId(inOut.getLibraryId());//库位
				stock.setQuantity(inOut.getQuantity());
				stock.setVersionNo(1);// 默认版本号 1
				this.addNewQuanty(stock, user);
			} else {
				stock.setQuantity(stock.getQuantity() + inOut.getQuantity());
				// 更新失败是否是 版本过期导致的
				try {
					this.updateStockQuantity(stock, user);
				} catch (Exception e) {
					// 更新版本 在提交数据
					addVersionUpdate(stock, inOut, user);
				}
			}
			// 插入详情表
			StockDetail stockDetail = new StockDetail();
			stockDetail.setMaterialId(inOut.getMaterialId());// 物料
			stockDetail.setWarchouseId(inOut.getWarchouseId());// 仓库
			stockDetail.setLibraryId(inOut.getLibraryId());// 库位
			stockDetail.setQuantity(inOut.getQuantity());
			stockDetail.setInNo(record.getInOutNo());
			stockDetail.setInTime(new Date());
			stockDetail.setInUser(user.getUserId());
			stockDetail.setUseableQuantity(inOut.getQuantity());
			stockDetail.setProductTime(inOut.getProductTime());
			stockDetail.setVersionNo(1);// 默认版本号 1
			// stockDetail.setExpirationTime(expirationTime);//先不计算过期时间
			stockDetailService.addStockDetail(stockDetail, user);
			// 插入出入库明细表
			StockInOut stockInOut = new StockInOut();
			stockInOut.setMaterialId(inOut.getMaterialId());// 物料
			stockInOut.setWarchouseId(inOut.getWarchouseId());// 仓库
			stockInOut.setLibraryId(inOut.getLibraryId());// 库位
			stockInOut.setQuantity(inOut.getQuantity());
			stockInOut.setInOutNo(record.getInOutNo());
			stockInOut.setProductTime(inOut.getProductTime());
			stockInOut.setInTime(new Date());
			stockInOut.setInUser(user.getUserId());
			stockInOut.setInOutType(RecordType.IN.getType());
			stockInOutService.addStockInOut(stockInOut, user);
		}

		// 资产入库
		for (InOutSerialize inOutSerialize : inOutSerializes) {
			// 查询仓库是否存在物料
			StockSerialize stockSerialize = new StockSerialize();
			stockSerialize.setId(UUIDUtil.getUUID());
			stockSerialize.setMaterialId(inOutSerialize.getMaterialId());// 物料
			stockSerialize.setWarchouseId(inOutSerialize.getWarchouseId());// 仓库
			stockSerialize.setLibraryId(inOutSerialize.getLibraryId());// 库位
			stockSerialize.setQuantity(inOutSerialize.getQuantity());// 数量
			stockSerialize.setSerializaNo(inOutSerialize.getSerializaNo());
			stockSerialize.setProductTime(inOutSerialize.getProductTime());
			stockSerialize.setVersionNo(1);// 默认版本号 1
			stockSerializeService.addStockSerialize(stockSerialize, user);
			// 插入详情表
			StockDetailSerialize stockDetail = new StockDetailSerialize();
			stockDetail.setMaterialId(inOutSerialize.getMaterialId());// 物料
			stockDetail.setWarchouseId(inOutSerialize.getWarchouseId());// 仓库
			stockDetail.setLibraryId(inOutSerialize.getLibraryId());// 库位
			stockDetail.setQuantity(inOutSerialize.getQuantity());
			stockDetail.setSerializaNo(inOutSerialize.getSerializaNo());
			stockDetail.setProductTime(inOutSerialize.getProductTime());
			stockDetail.setVersionNo(1);// 默认版本号 1
			stockDetail.setInNo(record.getInOutNo());
			stockDetail.setInTime(new Date());
			stockDetail.setInUser(user.getUserId());
			// stockDetail.setExpirationTime(expirationTime);//先不计算过期时间
			stockDetailSerializeService.addStockDetailSerialize(stockDetail, user);

			// 插入出入库详情
			StockInOutSerialize stockInOutSerialize = new StockInOutSerialize();
			stockInOutSerialize.setMaterialId(inOutSerialize.getMaterialId());// 物料
			stockInOutSerialize.setWarchouseId(inOutSerialize.getWarchouseId());// 仓库
			stockInOutSerialize.setLibraryId(inOutSerialize.getLibraryId());// 库位
			stockInOutSerialize.setQuantity(inOutSerialize.getQuantity());
			stockInOutSerialize.setSerializaNo(inOutSerialize.getSerializaNo());
			stockInOutSerialize.setInNo(record.getInOutNo());
			stockInOutSerialize.setInTime(new Date());
			stockInOutSerialize.setInUser(user.getUserId());
			stockInOutSerialize.setInOutType(RecordType.IN.getType());
			stockInOutSerializeService.addStockInOutSerialize(stockInOutSerialize, user);
		}

	}

	@Override
	@Transactional
	public void storeOut(InOutRecord record, List<InOut> inOuts, List<InOutSerialize> inOutSerializes, LoginUser user) {
		// Map<String,Stock> stockMap= new HashMap<>();
		// 物资入库
		for (InOut inOut : inOuts) {
			// 查询仓库是否存在物料
			Stock stock = this.queryStock(inOut.getMaterialId(), inOut.getWarchouseId(), user);
			// 库存空需要新增
			if (ObjectUtil.isNull(stock)) {
				throw new BussinessException(-1,
						String.format("物料:%s,在仓库:%s中不存在.", inOut.getMaterialId(), inOut.getWarchouseId()));
			} else {
				stock.setQuantity(stock.getQuantity() - inOut.getQuantity());
				// 更新失败是否是 版本过期导致的
				try {
					this.updateStockQuantity(stock, user);
				} catch (Exception e) {
					// 更新版本 在提交数据
					subtractVersionUpdate(stock, inOut, user);
				}
			}
			// 详情表出库 根据详情出库规则 现在默认为先进先出
			stockDetailOut(record, inOut, user);
			// 插入出入库明细表
			StockInOut stockInOut = new StockInOut();
			stockInOut.setMaterialId(inOut.getMaterialId());// 物料
			stockInOut.setWarchouseId(inOut.getWarchouseId());// 仓库
			stockInOut.setLibraryId(inOut.getLibraryId());// 库位
			stockInOut.setQuantity(inOut.getQuantity());
			stockInOut.setInOutNo(record.getInOutNo());
			stockInOut.setProductTime(inOut.getProductTime());
			stockInOut.setInTime(new Date());
			stockInOut.setInUser(user.getUserId());
			stockInOut.setInOutType(RecordType.OUT.getType());
			stockInOutService.addStockInOut(stockInOut, user);
		}
		StockSerialize stockSerialize = null;
		StockDetailSerialize stockDetailSerialize = null;
		// 资产出库库
		for (InOutSerialize inOutSerialize : inOutSerializes) {
			stockSerialize = stockSerializeService.queryStockSerialize(inOutSerialize.getMaterialId(),
					inOutSerialize.getSerializaNo(), inOutSerialize.getWarchouseId());
			if (ObjectUtil.isNull(stockSerialize)) {
				throw new BussinessException(-1, String.format("资产%s不在库存中,可能已被出库.", inOutSerialize.getSerializaNo()));
			}
			// 从资产表出库
			stockSerializeService.inOutSerializeOut(stockSerialize, user);

			stockDetailSerialize = stockDetailSerializeService.queryDetailStockSerialize(inOutSerialize.getMaterialId(),
					inOutSerialize.getSerializaNo(), inOutSerialize.getWarchouseId());
			if (ObjectUtil.isNull(stockDetailSerialize)) {
				throw new BussinessException(-1, String.format("资产%s不在库存中,可能已被出库.", inOutSerialize.getSerializaNo()));
			}
			// 从详情表出库
			stockDetailSerializeService.detailSerializeOut(stockDetailSerialize, user);
			// 插入出入库详情
			StockInOutSerialize stockInOutSerialize = new StockInOutSerialize();
			stockInOutSerialize.setMaterialId(inOutSerialize.getMaterialId());// 物料
			stockInOutSerialize.setWarchouseId(inOutSerialize.getWarchouseId());// 仓库
			stockInOutSerialize.setLibraryId(inOutSerialize.getLibraryId());// 库位
			stockInOutSerialize.setQuantity(inOutSerialize.getQuantity());
			stockInOutSerialize.setSerializaNo(inOutSerialize.getSerializaNo());
			stockInOutSerialize.setInNo(record.getInOutNo());
			stockInOutSerialize.setInTime(new Date());
			stockInOutSerialize.setInUser(user.getUserId());
			stockInOutSerialize.setInOutType(RecordType.OUT.getType());
			stockInOutSerializeService.addStockInOutSerialize(stockInOutSerialize, user);
		}
	}
	
	@Transactional(propagation = Propagation.NOT_SUPPORTED)
	public PageDto<StockDto> querySelectStock(StockDto params, PageDto<StockDto> page, LoginUser user) {
		params.setTenantId(user.getTenantId());
		params.setOrderByClause(page.getOrderBy());
		params.setLimitStart((page.getPage() - 1) * page.getPageSize());
		params.setLimitEnd(page.getPageSize());
		params.setIsDelete(DeleteEnum.USE.getValue());
		page.setTotal(selectStockMapper.countByExample(params));
		page.setRecords(selectStockMapper.selectByExample(params));
		return page;
	}
    
	@Override
	@Transactional(propagation = Propagation.NOT_SUPPORTED)
	public Stock selectStock(String materialId, String warchouseId) {
		StockExample example = new StockExample();
		example.createCriteria().andWarchouseIdEqualTo(warchouseId).andMaterialIdEqualTo(materialId)
				.andIsDeleteEqualTo(DeleteEnum.USE.getValue());
		List<Stock> list = stockMapper.selectByExample(example);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}
	
	
	/*******************************************************/

	/**
	 * 详情出库
	 * 
	 * @param record
	 * @param inOut
	 * @param user
	 */
	@Transactional
	private void stockDetailOut(InOutRecord record, InOut inOut, LoginUser user) {
		// 根据入库时间 进行出入库
		stockDetailOutWithInTime(record, inOut, user);
		// stockDetailOutWithPorductTime(record,inOut,user);
	}

	/**
	 * 根据入库时间进行出库
	 * 
	 * @param record
	 * @param inOut
	 */
	@Transactional
	private void stockDetailOutWithInTime(InOutRecord record, InOut inOut, LoginUser user) {
		// 已出库IDS
		List<String> outIds = new ArrayList<>();
		List<StockDetail> list = stockDetailService.queryStockDetail(inOut.getMaterialId(), record.getWarchouseId(),
				outIds, user);
		// 需要出库数量
		int outQuantity = inOut.getQuantity();
		for (StockDetail stockDetail : list) {
			// 剩余数量出库
			outQuantity = stockDetailService.stockDetailOut(outQuantity, stockDetail, user);
			if (outQuantity == 0)
				break;
		}
		if (outQuantity > 0) {
			throw new BussinessException(-1, String.format("物料:%s详情在仓库：%s出库失败,剩余数量:%d不能出库", inOut.getMaterialId(),
					record.getWarchouseId(), outQuantity));
		}
	}

	@Transactional
	private void stockDetailOutWithPorductTime(InOutRecord record, InOut inOut, LoginUser user) {
		// 已出库IDS
		List<String> outIds = new ArrayList<>();
		List<StockDetail> list = stockDetailService.queryStockDetail(inOut.getMaterialId(), record.getWarchouseId(),
				outIds, user);
		// 需要出库数量
		int outQuantity = inOut.getQuantity();
		for (StockDetail stockDetail : list) {
			// 剩余数量出库
			outQuantity = stockDetailService.stockDetailOut(outQuantity, stockDetail, user);
			if (outQuantity == 0)
				break;
		}
		if (outQuantity > 0) {
			throw new BussinessException(-1, String.format("物料:%s详情在仓库：%s出库失败,剩余数量:%d不能出库", inOut.getMaterialId(),
					record.getWarchouseId(), outQuantity));
		}
	}

	@Transactional
	private void addNewQuanty(Stock params, LoginUser user) {
		params.setTenantId(user.getTenantId());
		params.setIsDelete(DeleteEnum.USE.getValue());
		this.setUserForAdd(params, user);
		int count = stockMapper.insertSelective(params);
		if (count != 1) {
			throw new BussinessException(-1, ErrorMsg.INSERT_ERROR);
		}
	}

	/*
	 * 更新版本然后提交数据
	 */
	@Transactional
	private void addVersionUpdate(Stock stock, InOut inOut, LoginUser user) {
		// 查询仓库是否存在物料
		Stock newStock = this.queryStock(inOut.getMaterialId(), inOut.getWarchouseId(), user);
		// 版本号一样直接抛出异常
		if (stock.getVersionNo() == newStock.getVersionNo()) {
			throw new BussinessException(-1, String.format("物料号：%s入库异常.", inOut.getMaterialId()));
		}
		newStock.setQuantity(newStock.getQuantity() + inOut.getQuantity());
		// 更新失败是否是 版本过期导致的
		try {
			this.updateStockQuantity(newStock, user);
		} catch (Exception e) {
			// 更新版本 在提交数据
			addVersionUpdate(newStock, inOut, user);
		}
	}

	/*
	 * 更新版本然后提交数据
	 */
	@Transactional
	private void subtractVersionUpdate(Stock stock, InOut inOut, LoginUser user) {
		// 查询仓库是否存在物料
		Stock newStock = this.queryStock(inOut.getMaterialId(), inOut.getWarchouseId(), user);
		// 版本号一样直接抛出异常
		if (stock.getVersionNo() == newStock.getVersionNo()) {
			throw new BussinessException(-1, String.format("物料号：%s入库异常.", inOut.getMaterialId()));
		}
		newStock.setQuantity(newStock.getQuantity() - inOut.getQuantity());
		// 更新失败是否是 版本过期导致的
		try {
			this.updateStockQuantity(newStock, user);
		} catch (Exception e) {
			// 更新版本 在提交数据
			subtractVersionUpdate(newStock, inOut, user);
		}
	}

	/**
	 * 更新数量
	 * 
	 * @param stock
	 * @param user
	 */
	@Transactional
	private void updateStockQuantity(Stock stock, LoginUser user) {
		StockExample example = new StockExample();
		Criteria criteria = example.createCriteria();
		criteria.andVersionNoEqualTo(stock.getVersionNo());// 版本
		criteria.andStockIdEqualTo(stock.getStockId());
		stock.setVersionNo(stock.getVersionNo() + 1);// 版本加一
		this.setUserForUpdate(stock, user);
		int count = stockMapper.updateByExampleSelective(stock, example);
		if (count != 1) {
			logger.error("StockServiceImpl=>updateStockQuantity is error");
			throw new BussinessException(-1, ErrorMsg.UPDATE_ERROR);
		}

	}

	
	
	@Transactional(propagation = Propagation.NOT_SUPPORTED)
	private Stock queryStock(String materialId, String warchouseId, LoginUser user) {
		Stock stock = new Stock();
		stock.setMaterialId(materialId);
		stock.setWarchouseId(warchouseId);
		List<Stock> list = stockMapper.selectByRecord(stock);
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}

	

}
